/*
 * Copyright 1997-2006 Sun Microsystems, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */

package net.droidsolutions.droidcharts.awt;

import java.io.Serializable;

/**
 * The <code>Ellipse2D</code> class describes an ellipse that is defined by a
 * framing rectangle.
 * <p>
 * This class is only the abstract superclass for all objects which store a 2D
 * ellipse. The actual storage representation of the coordinates is left to the
 * subclass.
 * 
 * @author Jim Graham
 * @since 1.2
 */
public abstract class Ellipse2D extends RectangularShape {

	/**
	 * The <code>Float</code> class defines an ellipse specified in
	 * <code>float</code> precision.
	 * 
	 * @since 1.2
	 */
	public static class Float extends Ellipse2D implements Serializable {
		/**
		 * The X coordinate of the upper-left corner of the framing rectangle of
		 * this {@code Ellipse2D}.
		 * 
		 * @since 1.2
		 * @serial
		 */
		public float x;

		/**
		 * The Y coordinate of the upper-left corner of the framing rectangle of
		 * this {@code Ellipse2D}.
		 * 
		 * @since 1.2
		 * @serial
		 */
		public float y;

		/**
		 * The overall width of this <code>Ellipse2D</code>.
		 * 
		 * @since 1.2
		 * @serial
		 */
		public float width;

		/**
		 * The overall height of this <code>Ellipse2D</code>.
		 * 
		 * @since 1.2
		 * @serial
		 */
		public float height;

		/**
		 * Constructs a new <code>Ellipse2D</code>, initialized to location
		 * (0,&nbsp;0) and size (0,&nbsp;0).
		 * 
		 * @since 1.2
		 */
		public Float() {
		}

		/**
		 * Constructs and initializes an <code>Ellipse2D</code> from the
		 * specified coordinates.
		 * 
		 * @param x
		 *            the X coordinate of the upper-left corner of the framing
		 *            rectangle
		 * @param y
		 *            the Y coordinate of the upper-left corner of the framing
		 *            rectangle
		 * @param w
		 *            the width of the framing rectangle
		 * @param h
		 *            the height of the framing rectangle
		 * @since 1.2
		 */
		public Float(float x, float y, float w, float h) {
			setFrame(x, y, w, h);
		}

		/**
		 * {@inheritDoc}
		 * 
		 * @since 1.2
		 */
		public double getX() {
			return (double) x;
		}

		/**
		 * {@inheritDoc}
		 * 
		 * @since 1.2
		 */
		public double getY() {
			return (double) y;
		}

		/**
		 * {@inheritDoc}
		 * 
		 * @since 1.2
		 */
		public double getWidth() {
			return (double) width;
		}

		/**
		 * {@inheritDoc}
		 * 
		 * @since 1.2
		 */
		public double getHeight() {
			return (double) height;
		}

		/**
		 * {@inheritDoc}
		 * 
		 * @since 1.2
		 */
		public boolean isEmpty() {
			return (width <= 0.0 || height <= 0.0);
		}

		/**
		 * Sets the location and size of the framing rectangle of this
		 * <code>Shape</code> to the specified rectangular values.
		 * 
		 * @param x
		 *            the X coordinate of the upper-left corner of the specified
		 *            rectangular shape
		 * @param y
		 *            the Y coordinate of the upper-left corner of the specified
		 *            rectangular shape
		 * @param w
		 *            the width of the specified rectangular shape
		 * @param h
		 *            the height of the specified rectangular shape
		 * @since 1.2
		 */
		public void setFrame(float x, float y, float w, float h) {
			this.x = x;
			this.y = y;
			this.width = w;
			this.height = h;
		}

		/**
		 * {@inheritDoc}
		 * 
		 * @since 1.2
		 */
		public void setFrame(double x, double y, double w, double h) {
			this.x = (float) x;
			this.y = (float) y;
			this.width = (float) w;
			this.height = (float) h;
		}

		/**
		 * {@inheritDoc}
		 * 
		 * @since 1.2
		 */
		public Rectangle2D getBounds2D() {
			return new Rectangle2D.Float(x, y, width, height);
		}

		/*
		 * JDK 1.6 serialVersionUID
		 */
		private static final long serialVersionUID = -6633761252372475977L;
	}

	/**
	 * The <code>Double</code> class defines an ellipse specified in
	 * <code>double</code> precision.
	 * 
	 * @since 1.2
	 */
	public static class Double extends Ellipse2D implements Serializable {
		/**
		 * The X coordinate of the upper-left corner of the framing rectangle of
		 * this {@code Ellipse2D}.
		 * 
		 * @since 1.2
		 * @serial
		 */
		public double x;

		/**
		 * The Y coordinate of the upper-left corner of the framing rectangle of
		 * this {@code Ellipse2D}.
		 * 
		 * @since 1.2
		 * @serial
		 */
		public double y;

		/**
		 * The overall width of this <code>Ellipse2D</code>.
		 * 
		 * @since 1.2
		 * @serial
		 */
		public double width;

		/**
		 * The overall height of the <code>Ellipse2D</code>.
		 * 
		 * @since 1.2
		 * @serial
		 */
		public double height;

		/**
		 * Constructs a new <code>Ellipse2D</code>, initialized to location
		 * (0,&nbsp;0) and size (0,&nbsp;0).
		 * 
		 * @since 1.2
		 */
		public Double() {
		}

		/**
		 * Constructs and initializes an <code>Ellipse2D</code> from the
		 * specified coordinates.
		 * 
		 * @param x
		 *            the X coordinate of the upper-left corner of the framing
		 *            rectangle
		 * @param y
		 *            the Y coordinate of the upper-left corner of the framing
		 *            rectangle
		 * @param w
		 *            the width of the framing rectangle
		 * @param h
		 *            the height of the framing rectangle
		 * @since 1.2
		 */
		public Double(double x, double y, double w, double h) {
			setFrame(x, y, w, h);
		}

		/**
		 * {@inheritDoc}
		 * 
		 * @since 1.2
		 */
		public double getX() {
			return x;
		}

		/**
		 * {@inheritDoc}
		 * 
		 * @since 1.2
		 */
		public double getY() {
			return y;
		}

		/**
		 * {@inheritDoc}
		 * 
		 * @since 1.2
		 */
		public double getWidth() {
			return width;
		}

		/**
		 * {@inheritDoc}
		 * 
		 * @since 1.2
		 */
		public double getHeight() {
			return height;
		}

		/**
		 * {@inheritDoc}
		 * 
		 * @since 1.2
		 */
		public boolean isEmpty() {
			return (width <= 0.0 || height <= 0.0);
		}

		/**
		 * {@inheritDoc}
		 * 
		 * @since 1.2
		 */
		public void setFrame(double x, double y, double w, double h) {
			this.x = x;
			this.y = y;
			this.width = w;
			this.height = h;
		}

		/**
		 * {@inheritDoc}
		 * 
		 * @since 1.2
		 */
		public Rectangle2D getBounds2D() {
			return new Rectangle2D.Double(x, y, width, height);
		}

		/*
		 * JDK 1.6 serialVersionUID
		 */
		private static final long serialVersionUID = 5555464816372320683L;
	}

	/**
	 * This is an abstract class that cannot be instantiated directly.
	 * Type-specific implementation subclasses are available for instantiation
	 * and provide a number of formats for storing the information necessary to
	 * satisfy the various accessor methods below.
	 * 
	 * @see and.awt.geom.Ellipse2D.Float
	 * @see and.awt.geom.Ellipse2D.Double
	 * @since 1.2
	 */
	protected Ellipse2D() {
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @since 1.2
	 */
	public boolean contains(double x, double y) {
		// Normalize the coordinates compared to the ellipse
		// having a center at 0,0 and a radius of 0.5.
		double ellw = getWidth();
		if (ellw <= 0.0) {
			return false;
		}
		double normx = (x - getX()) / ellw - 0.5;
		double ellh = getHeight();
		if (ellh <= 0.0) {
			return false;
		}
		double normy = (y - getY()) / ellh - 0.5;
		return (normx * normx + normy * normy) < 0.25;
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @since 1.2
	 */
	public boolean intersects(double x, double y, double w, double h) {
		if (w <= 0.0 || h <= 0.0) {
			return false;
		}
		// Normalize the rectangular coordinates compared to the ellipse
		// having a center at 0,0 and a radius of 0.5.
		double ellw = getWidth();
		if (ellw <= 0.0) {
			return false;
		}
		double normx0 = (x - getX()) / ellw - 0.5;
		double normx1 = normx0 + w / ellw;
		double ellh = getHeight();
		if (ellh <= 0.0) {
			return false;
		}
		double normy0 = (y - getY()) / ellh - 0.5;
		double normy1 = normy0 + h / ellh;
		// find nearest x (left edge, right edge, 0.0)
		// find nearest y (top edge, bottom edge, 0.0)
		// if nearest x,y is inside circle of radius 0.5, then intersects
		double nearx, neary;
		if (normx0 > 0.0) {
			// center to left of X extents
			nearx = normx0;
		} else if (normx1 < 0.0) {
			// center to right of X extents
			nearx = normx1;
		} else {
			nearx = 0.0;
		}
		if (normy0 > 0.0) {
			// center above Y extents
			neary = normy0;
		} else if (normy1 < 0.0) {
			// center below Y extents
			neary = normy1;
		} else {
			neary = 0.0;
		}
		return (nearx * nearx + neary * neary) < 0.25;
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @since 1.2
	 */
	public boolean contains(double x, double y, double w, double h) {
		return (contains(x, y) && contains(x + w, y) && contains(x, y + h) && contains(
				x + w, y + h));
	}

	/**
	 * Returns an iteration object that defines the boundary of this
	 * <code>Ellipse2D</code>. The iterator for this class is multi-threaded
	 * safe, which means that this <code>Ellipse2D</code> class guarantees that
	 * modifications to the geometry of this <code>Ellipse2D</code> object do
	 * not affect any iterations of that geometry that are already in process.
	 * 
	 * @param at
	 *            an optional <code>AffineTransform</code> to be applied to the
	 *            coordinates as they are returned in the iteration, or
	 *            <code>null</code> if untransformed coordinates are desired
	 * @return the <code>PathIterator</code> object that returns the geometry of
	 *         the outline of this <code>Ellipse2D</code>, one segment at a
	 *         time.
	 * @since 1.2
	 */
	public PathIterator getPathIterator(AffineTransform at) {
		return new EllipseIterator(this, at);
	}

	/**
	 * Returns the hashcode for this <code>Ellipse2D</code>.
	 * 
	 * @return the hashcode for this <code>Ellipse2D</code>.
	 * @since 1.6
	 */
	public int hashCode() {
		long bits = java.lang.Double.doubleToLongBits(getX());
		bits += java.lang.Double.doubleToLongBits(getY()) * 37;
		bits += java.lang.Double.doubleToLongBits(getWidth()) * 43;
		bits += java.lang.Double.doubleToLongBits(getHeight()) * 47;
		return (((int) bits) ^ ((int) (bits >> 32)));
	}

	/**
	 * Determines whether or not the specified <code>Object</code> is equal to
	 * this <code>Ellipse2D</code>. The specified <code>Object</code> is equal
	 * to this <code>Ellipse2D</code> if it is an instance of
	 * <code>Ellipse2D</code> and if its location and size are the same as this
	 * <code>Ellipse2D</code>.
	 * 
	 * @param obj
	 *            an <code>Object</code> to be compared with this
	 *            <code>Ellipse2D</code>.
	 * @return <code>true</code> if <code>obj</code> is an instance of
	 *         <code>Ellipse2D</code> and has the same values;
	 *         <code>false</code> otherwise.
	 * @since 1.6
	 */
	public boolean equals(Object obj) {
		if (obj == this) {
			return true;
		}
		if (obj instanceof Ellipse2D) {
			Ellipse2D e2d = (Ellipse2D) obj;
			return ((getX() == e2d.getX()) && (getY() == e2d.getY())
					&& (getWidth() == e2d.getWidth()) && (getHeight() == e2d
					.getHeight()));
		}
		return false;
	}
}
